#include "PreCompile.hpp"
#include "NumberParsers.hpp"

#define NUM_ELEMS(a) (sizeof(a) / sizeof(a[0]))  

// C standard:
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
// paragraph 6.4.4

namespace CPreProc
{
  static const int integer_sizes[] = 
  {
    sizeof(int) * 8,
    sizeof(long int) * 8,
    sizeof(long long int) * 8,
  };

  template <class T>
  T big_shift(T i,int shift)
  {
    const int shifts_per_it = sizeof(T) * 8 - 1;
    while(shift)
    {
      int cur_num_shifts = std::min(shift,shifts_per_it);
      i <<= cur_num_shifts;
      shift -= cur_num_shifts;
    }
    return i;
  }

  template <class T>
  void parse_simple_integer(Tokenizer &tokenizer,T &result,bool &overflow)
  {
    if(tokenizer.compare_advance_on_equal("0x") || tokenizer.compare_advance_on_equal("0X"))
    {
      // it's a hex value

      while(!tokenizer.end_of_data())
      {
        T prev_res = result;
        
        if(tokenizer.cur() >= '0' && tokenizer.cur() <= '9')
          result = result * 16 + tokenizer.cur() - '0';
        else if(tokenizer.cur() >= 'a' && tokenizer.cur() <= 'f')
          result = result * 16 + tokenizer.cur() - 'a' + 10;
        else if(tokenizer.cur() >= 'A' && tokenizer.cur() <= 'F')
          result = result * 16 + tokenizer.cur() - 'A' + 10;
        else
          break;

        if(prev_res > unsigned_int_max<T>() / 16)
        {
          overflow = true;
          break;
        }

        tokenizer.advance();
      }
    }
    else if(tokenizer.cur() == '0')
    {
      // it's octal

      while(!tokenizer.end_of_data())
      {
        T prev_res = result;
        
        if(tokenizer.cur() >= 0 && tokenizer.cur() <= '7')
          result = result * 8 + tokenizer.cur() - '0';
        else
          break;
        
        if(prev_res > unsigned_int_max<T>() / 8)
        {
          overflow = true;
          break;
        }

        tokenizer.advance();
      }
    }
    else
    {
      // it's decimal
      
      while(!tokenizer.end_of_data())
      {
        T prev_res = result;
        
        if(tokenizer.cur() >= '0' && tokenizer.cur() <= '9')
        {
          result = result * 10 + tokenizer.cur() - '0';
          tokenizer.advance();
        }
        else
          break;

        if(result > unsigned_int_max<T>() / 10)
        {
          overflow = true;
          break;
        }
      }
    }
  }

  template <class T>
  void calc_min_size(T val,bool &is_unsigned,int &num_long_suffixes)
  {
    // find out the size
    is_unsigned = false;
    num_long_suffixes = -1;
    for(int i = 0;i < 3;i++)
    {
      // this shouldn't happen, because 
      if(integer_sizes[i] > local_value_num_bits<T>())
      {
        // the type we are using of this local platform, is smaller than integer_sizes[i], so 
        // we are sure that the value fits in the current integer_sizes[i]. 
        // Otherwise, we would have switched to BigInteger already.
      }

      unsigned int cur_unsigned_max = big_shift<unsigned int>(1,integer_sizes[i]) - 1;
      unsigned int cur_signed_max = cur_unsigned_max / 2;
      if(val <= cur_signed_max)
      {
        num_long_suffixes = i;
        is_unsigned = false;
        break;
      }
      else if(val <= cur_unsigned_max)
      {
        num_long_suffixes = i;
        is_unsigned = true;
        break;
      }
    }
  }

  bool try_parse_number(Tokenizer &tokenizer,ParsedNumber &out)
  {
    if(tokenizer.cur() < '0' || tokenizer.cur() > '9')
      return false;

    unsigned int result = 0;
    bool overflow = false;
    
    Tokenizer backup = tokenizer;
    parse_simple_integer<unsigned int>(tokenizer,result,overflow);
    if(overflow)
    {
      tokenizer = backup;

      BigUInt big_value;
      parse_simple_integer<BigUInt>(tokenizer,big_value,overflow);
      assert(!overflow);    // should be impossible with BigUInt
    }

    bool min_is_unsigned = false;
    int min_num_long_suffixes = -1;
    calc_min_size(result,min_is_unsigned,min_num_long_suffixes);

    if(min_num_long_suffixes == -1)
    {
      // actual overflow, according to the current target system.
      assert(!"not implemented yet");
    }

    if(tokenizer.end_of_data())
    {
      ParsedNumberType number_type = (ParsedNumberType)(min_num_long_suffixes * 2 + (min_is_unsigned ? 1 : 0));
      out.set(number_type,result);
      return true;
    }

    bool specified_is_unsigned = false;
    int specified_num_long_suffixes = -1;

    if(tokenizer.cur() == 'u' || tokenizer.cur() == 'U')
    {
      specified_is_unsigned = true;
      tokenizer.advance();
    }

    if(!tokenizer.end_of_data())
    {
      if(tokenizer.compare_advance_on_equal("ll") ||
        tokenizer.compare_advance_on_equal("LL"))
      {
        specified_num_long_suffixes = 2;
      }
      else if(tokenizer.cur() == 'L' || tokenizer.cur() == 'l')
      {
        specified_num_long_suffixes = 1;
        tokenizer.advance();
      }

      if(!specified_is_unsigned && !tokenizer.end_of_data() && 
        (tokenizer.cur() == 'u' || tokenizer.cur() == 'U'))
      {
        specified_is_unsigned = true;
        tokenizer.advance();
      }
    }

    int num_long_suffixes = num_long_suffixes = std::max(min_num_long_suffixes,specified_num_long_suffixes);
    bool is_unsigned;
    if(specified_is_unsigned)
      is_unsigned = true;
    else
      is_unsigned = num_long_suffixes == min_num_long_suffixes;
    
    ParsedNumberType number_type = (ParsedNumberType)(num_long_suffixes * 2 + (is_unsigned ? 1 : 0));
    out.set(number_type,result);

    return true;
  }
}

